﻿using System;
using System.Diagnostics;
using System.Net;
using System.Threading;
using System.Windows.Forms;

namespace Chromium_Updater
{
    public partial class MainForm : Form
    {
        #region Variables

        Mutex appMutex;
        //
        CUpdater updaterWorker;
        UpdaterForm selfUpdater;
        SettingsForm settingsForm;
        CUpdatesForm CUpdatesForm;
        //
        private ActionState check = new ActionState("Check", true);
        private ActionState checking = new ActionState("Checking", false);
        private ActionState download = new ActionState("Download", true);
        private ActionState downloading = new ActionState("Downloading", false);
        private ActionState install = new ActionState("Install", true);
        private ActionState installing = new ActionState("Installing", false);
        //
        private delegate void changeNextActionD(ActionState action);
        private delegate void changeValueD(string value);
        private delegate void displayStatusD(string status, int refresh);
        private delegate void permanentStatusD(string status);
        private delegate void emptyD();

        #endregion

        #region Form Events

        #region Constructor/Load/Close

        public MainForm(string[] args)
        {
            try
            {
                appMutex = Mutex.OpenExisting("chromium-updater-v2");
                //
                if (args.Length == 1)
                {
                    Process[] procs = Process.GetProcessesByName(Process.GetCurrentProcess().ProcessName);
                    if (procs.Length > 0)
                    {
                        foreach (Process proc in procs)
                        {
                            if (proc.MainWindowHandle != this.Handle) { MessageHelper.sendWindowString(proc.MainWindowHandle, "path-" + args[0]); }
                        }
                    }
                }
                else { MessageBox.Show("Only one instance of Chromium Updater may exist"); }
                Environment.Exit(0);
            }
            catch (WaitHandleCannotBeOpenedException)
            {
                appMutex = new Mutex(false, "chromium-updater-v2");
            }
            //
            Settings.load();
            if (args.Length == 1)
            {
                IniFile cuu = new IniFile(args[0]);
                new CUUForm(cuu.GetString("urls", "latest_revision_url", ""), cuu.GetString("urls", "specific_revision_url", "")).ShowDialog();
                cuu = null;
            }
            //
            InitializeComponent();
        }

        private void MainForm_Load(object sender, EventArgs e)
        {
            if (Settings.Start_Hidden)
            {
                this.WindowState = FormWindowState.Minimized;
            }
            //
            selfUpdater = new UpdaterForm(1);
            selfUpdater.doneChecking += new updateAvailable(selfUpdater_doneChecking);
            selfUpdater.checkForUpdateAsync();
            //
            updaterWorker = new CUpdater();
            updaterWorker.checkReturn += new updateCheck(updaterWorker_checkCompleted);
            updaterWorker.progressChanged += new downloadProgressChanged(updaterWorker_progressChanged);
            updaterWorker.downloadReturn += new Download(updaterWorker_fileDownloaded);
            updaterWorker.installReturn += new Install(updaterWorker_finished);
            //
            if (!FileAssociation.IsAssociated(".cuu"))
            {
                FileAssociation.Associate(".cuu", "Chromium Updater", "Updater URLS", "", Application.ExecutablePath);
            }
            //
            label4.Text = Settings.Current_Revision.ToString();
            //
            if (Settings.Check_Interval > 0)
            {
                timer1.Interval = ((Settings.Check_Interval * 60) * 1000);
                timer1.Enabled = true;
            }
            beginUpdate();
        }

        private void MainForm_FormClosing(object sender, FormClosingEventArgs e)
        {
            if (updaterWorker.isBusy)
            {
                if (MessageBox.Show("You are currently in the process of updating. Are you sure you want to exit?", "Chromium Updater", MessageBoxButtons.YesNo) == DialogResult.No)
                {
                    e.Cancel = true;
                    return;
                }
            }
            else if (MessageBox.Show("Are you sure you want to exit Chromium Updater?", "Chromium Updater", MessageBoxButtons.YesNo) == DialogResult.No)
            {
                e.Cancel = true;
                return;
            }
            //
            notifyIcon1.Dispose();
            appMutex.WaitOne();
            appMutex.ReleaseMutex();
            Environment.Exit(0);
        }

        private void exitToolStripMenuItem_Click(object sender, EventArgs e)
        {
            this.Close();
        }

        #endregion

        #region Overrides

        /*protected override void WndProc(ref Message m)
        {
            //Copy Data Struct (I guess thats the way MSFT wants you to pass strings)
            if (m.Msg == MessageHelper.WM_COPYDATA)
            {
                //MessageBox.Show(mes);
            }
            base.WndProc(ref m);
        }*/

        #endregion

        #region Self Updater

        private void selfUpdater_doneChecking(bool updateAvailable, AppUpdate[] availableUpdates)
        {
            // TODO: Add support for Errors in Updater class so that we can show it to user instead of just not doing anything
            // Could you imagine if someones app was just doing this everytime they start up and not succedding everytime? That would be a fail
            if (updateAvailable)
            {
                string mes = "";
                if (availableUpdates.Length > 1) { mes = "There is " + availableUpdates.Length.ToString() + " updates available for " + Application.ProductName + ". Would you like to update now?"; }
                else { mes = "There is an update available for " + Application.ProductName + ". Would you like to update now?"; }
                if (MessageBox.Show(mes, Application.ProductName, MessageBoxButtons.YesNo) == DialogResult.Yes)
                {
                    if (Settings.Show_Updater_Changes) { selfUpdater.Show(); }
                    else { selfUpdater.downloadUpdateAsync(); }
                }
            }
        }

        #endregion

        #region SettingsForm

        private void button2_Click(object sender, EventArgs e)
        {
            if (!SettingsForm.isOpen)
            {
                settingsForm = new SettingsForm();
                settingsForm.FormClosed += new FormClosedEventHandler(settingsForm_FormClosed);
                settingsForm.Show();
            }
            else { settingsForm.BringToFront(); }
        }

        private void settingsToolStripMenuItem_Click(object sender, EventArgs e)
        {
            button2_Click(this, null);
        }

        private void settingsForm_FormClosed(object sender, FormClosedEventArgs e)
        {
            if (timer1.Interval != ((Settings.Check_Interval * 60) * 1000))
            {
                timer1.Interval = ((Settings.Check_Interval * 60) * 1000);
            }
            if ((Settings.Auto_Download) && (button1.Text == "Download"))
            {
                beginDownload();
            }
        }

        #endregion

        #region CUpdatesForm

        private void label4_Click(object sender, EventArgs e)
        {
            if (!CUpdatesForm.isOpen)
            {
                CUpdatesForm = new CUpdatesForm();
                CUpdatesForm.Show();
            }
            else { CUpdatesForm.BringToFront(); }
        }

        #endregion

        #region Main Button logic

        private void button1_Click(object sender, EventArgs e)
        {
            switch (button1.Text)
            {
                case "Check":
                    beginUpdate();
                    break;
                case "Download":
                    beginDownload();
                    break;
                case "Install":
                case "Reinstall":
                    beginInstall();
                    break;
            }
        }

        private void timer1_Tick(object sender, EventArgs e)
        {
            beginUpdate();
        }

        #endregion

        #region Form States (Minimize/etc.)

        private void MainForm_Resize(object sender, EventArgs e)
        {
            if (WindowState == FormWindowState.Minimized)
            {
                this.Opacity = 0;
                this.ShowInTaskbar = false;
                this.notifyIcon1.Visible = true;
            }
        }

        private void notifyIcon1_MouseDoubleClick(object sender, MouseEventArgs e)
        {
            this.WindowState = FormWindowState.Normal;
            this.ShowInTaskbar = true;
            this.Opacity = 100;
            this.notifyIcon1.Visible = false;
            this.BringToFront();
        }

        #endregion

        #region Context Menu

        private void checkToolStripMenuItem_Click(object sender, EventArgs e)
        {
            beginUpdate();
        }

        private void downloadToolStripMenuItem_Click(object sender, EventArgs e)
        {
            beginDownload();
        }

        private void installToolStripMenuItem_Click(object sender, EventArgs e)
        {
            beginInstall();
        }

        #endregion

        #endregion

        #region Delegated functions

        #region Form actions

        #region Local/Remote Version

        private void changeLocalVersion(string value)
        {
            if (InvokeRequired)
            {
                BeginInvoke(new changeValueD(changeLocalVersion), new object[] { value });
                return;
            }
            //
            label4.Text = value;
        }

        private void changeLatestVersion(string value)
        {
            if (InvokeRequired)
            {
                BeginInvoke(new changeValueD(changeLatestVersion), new object[] { value });
                return;
            }
            //
            label6.Text = value;
        }

        #endregion

        #region Action Button

        private void changeNextAction(ActionState action)
        {
            if (InvokeRequired)
            {
                BeginInvoke(new changeNextActionD(changeNextAction), new object[] { action });
                return;
            }
            //
            button1.Text = action.Action;
            button1.Enabled = action.Enabled;
            //
            if (action.Action == check.Action)
            {
                button1.ContextMenuStrip = null;
                toolStripSeparator1.Visible = true;
                checkToolStripMenuItem1.Visible = true;
            }
            else if (action.Action == checking.Action)
            {
                button1.ContextMenuStrip = null;
                toolStripSeparator1.Visible = false;
                checkToolStripMenuItem1.Visible = false;
                updateToolStripMenuItem.Visible = false;
                installToolStripMenuItem1.Visible = false;
            }
            else if (action.Action == download.Action)
            {
                installToolStripMenuItem1.Visible = false;
                installToolStripMenuItem.Visible = false;
                button1.ContextMenuStrip = contextMenuStrip2;
                toolStripSeparator1.Visible = true;
                checkToolStripMenuItem1.Visible = true;
                checkToolStripMenuItem.Visible = true;
                downloadToolStripMenuItem.Visible = true;
                updateToolStripMenuItem.Visible = true;
            }
            else if (action.Action == downloading.Action)
            {
                button1.ContextMenuStrip = null;
                toolStripSeparator1.Visible = false;
                checkToolStripMenuItem1.Visible = false;
                updateToolStripMenuItem.Visible = false;
                installToolStripMenuItem1.Visible = false;
            }
            else if (action.Action == install.Action)
            {
                button1.ContextMenuStrip = contextMenuStrip2;
                toolStripSeparator1.Visible = true;
                checkToolStripMenuItem1.Visible = true;
                checkToolStripMenuItem.Visible = true;
                downloadToolStripMenuItem.Visible = true;
                updateToolStripMenuItem.Visible = true;
                installToolStripMenuItem1.Visible = true;
                installToolStripMenuItem.Visible = true;
            }
            else if (action.Action == installing.Action)
            {
                button1.ContextMenuStrip = null;
                toolStripSeparator1.Visible = false;
                checkToolStripMenuItem1.Visible = false;
                updateToolStripMenuItem.Visible = false;
                installToolStripMenuItem1.Visible = false;
            }
        }

        struct ActionState
        {
            private string _action;
            private bool _enabled;

            public ActionState(string action_, bool enabled_)
            {
                _action = action_;
                _enabled = enabled_;
            }

            public string Action
            {
                get { return _action; }
            }

            public bool Enabled
            {
                get { return _enabled; }
            }
        }

        #endregion

        #region Status

        private void displayStatus(string value, int refresh = 5)
        {
            if (InvokeRequired)
            {
                BeginInvoke(new displayStatusD(displayStatus), new object[] { value, refresh });
                return;
            }
            //
            permanentStatus(value);
            StatusLabelRefresh.Interval = refresh * 1000;
            StatusLabelRefresh.Enabled = true;
        }

        private void permanentStatus(string value)
        {
            if (InvokeRequired)
            {
                BeginInvoke(new permanentStatusD(permanentStatus), new object[] { value });
                return;
            }
            //
            StatusLabelRefresh.Enabled = false;
            toolStripStatusLabel1.Text = value;
            notifyIcon1.Text = value;
        }

        private void StatusLabelRefresh_Tick(object sender, EventArgs e)
        {
            StatusLabelRefresh.Enabled = false;
            toolStripStatusLabel1.Text = "";
            notifyIcon1.Text = "Chromium Updater";
        }
        #endregion

        #endregion

        #region Misc

        private void delegatedClose()
        {
            if (InvokeRequired)
            {
                BeginInvoke(new emptyD(delegatedClose));
                return;
            }
            //
            this.Close();
        }

        #endregion

        #endregion

        #region Update Processor

        #region Checker

        private void beginUpdate()
        {
            changeNextAction(checking);
            permanentStatus("");
            //
            updaterWorker.checkForUpdateAsync();
        }

        private void updaterWorker_checkCompleted(CheckResult r)
        {
            switch (r)
            {
                case CheckResult.UpdateAvailable:
                    changeLatestVersion(updaterWorker.latestRevision.ToString());
                    if (Settings.Auto_Download) { beginDownload(); }
                    else { changeNextAction(download); }
                    break;
                case CheckResult.NoUpdateAvailable:
                    changeLatestVersion(updaterWorker.latestRevision.ToString());
                    displayStatus("You have the latest version of Chromium");
                    changeNextAction(check);
                    break;
                case CheckResult.InvalidURL:
                    if (MessageBox.Show("The updater URLs are invalid. Would you like to corret it now?", "Chromium Updater", MessageBoxButtons.YesNo) == DialogResult.Yes)
                        new SettingsForm().Show();
                    changeNextAction(check);
                    break;
                case CheckResult.NoInternet:
                    permanentStatus("You are not connected to the Internet");
                    changeNextAction(check);
                    break;
            }
        }

        #endregion

        #region Downloader

        private void beginDownload()
        {
            if (InvokeRequired)
            {
                BeginInvoke(new emptyD(beginDownload));
                return;
            }
            //
            changeNextAction(downloading);
            toolStripProgressBar1.Visible = true;
            //
            updaterWorker.downloadUpdateAsync();
        }

        private void updaterWorker_progressChanged(DownloadProgressChangedEventArgs e)
        { 
            toolStripProgressBar1.Value = e.ProgressPercentage;
            toolStripStatusLabel1.Text = e.ProgressPercentage.ToString() + "%";
        }

        private void updaterWorker_fileDownloaded(DownloadResult r)
        {
            switch (r)
            {
                case DownloadResult.Success:
                    displayStatus("Revision " + '"' + updaterWorker.latestRevision + '"' + "has been downloaded");
                    beginInstall();
                    break;
                case DownloadResult.InvalidURL:
                    if (MessageBox.Show("The updater URLs are invalid. Would you like to corret it now?", "Chromium Updater", MessageBoxButtons.YesNo) == DialogResult.Yes)
                        new SettingsForm().Show();
                    changeNextAction(check);
                    break;
                case DownloadResult.NoInternet:
                    permanentStatus("You are not connected to the Internet");
                    changeNextAction(check);
                    break;
                case DownloadResult.FileInUse:
                    MessageBox.Show("The file that we are downloading to is in use.");
                    changeNextAction(check);
                    break;
            }
            //
            toolStripProgressBar1.Value = 0;
            toolStripProgressBar1.Visible = false;
        }

        #endregion

        #region Installer

        private void beginInstall()
        {
            changeNextAction(installing);
            //
            updaterWorker.installUpdateAsync();
        }

        private void updaterWorker_finished(InstallResult r)
        {
            switch (r)
            {
                case InstallResult.Success:
                    changeLocalVersion(updaterWorker.latestRevision.ToString());
                    displayStatus("Revision " + '"' + updaterWorker.latestRevision + '"' + " installed succesfully");
                    changeNextAction(check);
                    break;
                case InstallResult.Fail:
                    MessageBox.Show("There was trouble running the update file.");
                    changeNextAction(install);
                    break;
                case InstallResult.ChromiumRunning:
                    displayStatus("Chromium is currently running");
                    changeNextAction(install);
                    break;
            }
        }

        #endregion

        #endregion
    }
}
